/**
 *    Copyright 2009-2021 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.scripting.xmltags;

import java.util.Map;

import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.Configuration;

/**
 * @author Clinton Begin
 */
public class DynamicSqlSource implements SqlSource {

	private final Configuration configuration;
	private final SqlNode rootSqlNode;

	public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
		this.configuration = configuration;
		this.rootSqlNode = rootSqlNode;
	}

	/**
	 *
	 * @param parameterObject 用户传入实参
	 */
	@Override
	public BoundSql getBoundSql(Object parameterObject) {
		// 创建 DynamicContext 对象，parameterObject 是用户传入实参
		DynamicContext context = new DynamicContext(configuration, parameterObject);
		// 通过调用 rootSqlNode.apply() 方法调用整个树型结构中全部 SqlNode.apply() 方法。
		// 每个 SqlNode的apply()方法都将解析得到SQL语句片段追加到 context 中，最终通过 context.getSql()得到完整的 Sql 语句
		rootSqlNode.apply(context);
		// 创建SQL信息解析器，
		SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
		// 获取入参类型
		Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
		// 执行解析：将带有#{}的SQL语句进行解析，然后封装到StaticSqlSource中 (将 SQL 语句中的 “#{}”占为符替换成"?"占位符)
		SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
		// 将解析后的SQL语句还有入参绑定到一起（封装到一个对象中，此时还没有将参数替换到SQL占位符?）
		// [将 DynamicContext.bindings 中的参数信息复制到其 additionalParameter 集合中保存]
		BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
		for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
			boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
		}
		return boundSql;
	}

}
